package com.neusoft.neuiotms.dcom.service.impl;

import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.LongSummaryStatistics;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import javax.persistence.EntityNotFoundException;
import javax.transaction.Transactional;

import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import com.google.common.collect.Maps;
import com.neusoft.bizcore.auth.common.bean.UserBean;
import com.neusoft.bizcore.web.constant.ExceptionConstants;
import com.neusoft.bizcore.web.dto.result.ResultDTO;
import com.neusoft.bizcore.web.exception.UnitedException;
import com.neusoft.bizcore.web.support.Searchable;
import com.neusoft.bizcore.web.utils.EasyPoiExcelUtil;
import com.neusoft.neuiotms.dcom.bean.ProjectUserBean;
import com.neusoft.neuiotms.dcom.constants.DCOMConstants;
import com.neusoft.neuiotms.dcom.converter.TransferWeeklyConverter;
import com.neusoft.neuiotms.dcom.dto.InstitutionsDTO;
import com.neusoft.neuiotms.dcom.dto.TransferWeeklyDTO;
import com.neusoft.neuiotms.dcom.model.TransferWeekly;
import com.neusoft.neuiotms.dcom.model.WorkDaily;
import com.neusoft.neuiotms.dcom.repository.TransferWeeklyRepository;
import com.neusoft.neuiotms.dcom.repository.WorkDailyRepository;
import com.neusoft.neuiotms.dcom.service.InstitutionsService;
import com.neusoft.neuiotms.dcom.service.TransferWeeklyService;
import com.neusoft.neuiotms.dcom.util.PermUtils;
import com.neusoft.neuiotms.dcom.vo.TransferWeeklyExportVO;
import com.neusoft.neuiotms.dcom.vo.TransferWeeklyImportVO;
import com.neusoft.neuiotms.micro.dcam.DcamMicroService;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.TemplateExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class TransferWeeklyServiceImpl implements TransferWeeklyService {

    @Autowired
    private TransferWeeklyRepository transferWeeklyRepository;
    @Autowired
    private TransferWeeklyConverter transferWeeklyConverter;
    @Autowired
    private InstitutionsService institutionsService;
    @Autowired
    private WorkDailyRepository workDailyRepository;
    @Autowired
    private DcamMicroService dcamMicroService;

    @Override
    public List<TransferWeeklyDTO> index(final Searchable searchable, final UserBean userBean) {
        final List<InstitutionsDTO> institutions = this.institutionsService.getPermInstitutions(userBean);
        final List<String> institutionCodeArray =
                institutions.stream().map(it -> it.getCode()).collect(Collectors.toList());
        searchable.put("institutionCodeArray", institutionCodeArray);
        final List<TransferWeekly> result = this.transferWeeklyRepository.index(searchable);
        return this.transferWeeklyConverter.toListDTO(result);
    }

    @Override
    public Page<TransferWeeklyDTO> search(final Pageable pageable, final Searchable searchable,
            final UserBean userBean) {
        final List<InstitutionsDTO> institutions = this.institutionsService.getPermInstitutions(userBean);
        final List<String> institutionCodeArray =
                institutions.stream().map(it -> it.getCode()).collect(Collectors.toList());
        searchable.put("institutionCodeArray", institutionCodeArray);
        final Page<TransferWeekly> result = this.transferWeeklyRepository.search(pageable, searchable);
        return this.transferWeeklyConverter.toPageDTO(result);
    }

    @Transactional
    @Override
    public TransferWeeklyDTO create(final TransferWeeklyDTO dto) {
        final TransferWeekly model = this.transferWeeklyConverter.toModel(dto);
        this.transferWeeklyRepository.save(model);
        return this.transferWeeklyConverter.toDTO(model);
    }

    @Transactional
    @Override
    public TransferWeeklyDTO update(final Long id, final TransferWeeklyDTO dto) {
        TransferWeekly model;
        try {
            model = this.transferWeeklyRepository.getOne(id);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }
        model = this.transferWeeklyConverter.toModel(dto);
        this.transferWeeklyRepository.save(model);
        return this.transferWeeklyConverter.toDTO(model);
    }

    @Override
    public TransferWeeklyDTO details(final Long id) {
        final TransferWeekly model;
        try {
            model = this.transferWeeklyRepository.getOne(id);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }
        return this.transferWeeklyConverter.toDTO(model);
    }

    @Transactional
    @Override
    public void delete(final Long id) {
        try {
            this.transferWeeklyRepository.deleteById(id);
        } catch (final EntityNotFoundException e) {
            TransferWeeklyServiceImpl.log.error(e.getMessage(), e);
            throw new UnitedException(e.getMessage(), new Object[] {id });
        }
    }

    @Override
    public Workbook export(final Searchable searchable, final UserBean user) {
        final Map<String, Object> value = this.getData(searchable, user);
        final TemplateExportParams params = new TemplateExportParams(
                "excel/transfer.xlsx");
        //        params.setColForEach(true);
        final Workbook workbook = ExcelExportUtil.exportExcel(params, value);
        //        PoiMergeCellUtil.mergeCells(workbook.getSheetAt(0), 1, 0, 1);
        return workbook;

    }

    private Map<String, Object> getData(final Searchable searchable, final UserBean user) {
        final Map<String, Object> map = new HashMap<>();
        final List<TransferWeeklyDTO> dtos = this.index(searchable, user);
        map.put("all", dtos.size());
        final Integer finish = dtos.stream().filter(t -> t.getSchedule() == 100).collect(Collectors.toList()).size();
        map.put("finish", finish);
        final List<TransferWeeklyExportVO> list = new ArrayList<>();
        for (int i = 0; i < dtos.size(); i++) {
            final TransferWeeklyDTO dto = dtos.get(i);
            //            final String thisWeek = this.getThisWeek(dto.getUnitCode());
            final TransferWeeklyExportVO vo = this.transferWeeklyConverter.toVo(dto);
            vo.setNo(i + 1);
            //            vo.setThisWeek(thisWeek);
            list.add(vo);
        }
        map.put("list", list);
        return map;
    }

    @Deprecated
    @SuppressWarnings("unused")
    private String getThisWeek(final String unitCode) {
        final SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy年MM月dd日");
        final SimpleDateFormat sdfTime = new SimpleDateFormat("HH:mm");
        final Searchable searchable = new Searchable();
        searchable.put("unitCode", unitCode);
        final Map<String, Date> time = this.getTimeInterval(new Date());
        searchable.put("afEqReportDate", time.get("weekBegin").getTime());
        searchable.put("beEqReportDate", time.get("weekEnd").getTime());
        final List<WorkDaily> lists = this.workDailyRepository.index(searchable);
        final StringBuffer strBuffer = new StringBuffer();
        lists.forEach(t -> {
            strBuffer.append(sdfDate.format(t.getDailyReportDate())).append(sdfTime.format(t.getServiceStartTime()))
                    .append("至").append(sdfTime.format(t.getServiceEndTime())).append(" ").append(t.getJobContent())
                    .append("\r\n");

        });
        return strBuffer.toString();

    }

    @Deprecated
    private Map<String, Date> getTimeInterval(final Date date) {
        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        final Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        // 判断要计算的日期是否是周日，如果是则减一天计算周六的，否则会出问题，计算到下一周去了
        final int dayWeek = cal.get(Calendar.DAY_OF_WEEK);// 获得当前日期是一个星期的第几天
        if (1 == dayWeek) {
            cal.add(Calendar.DAY_OF_MONTH, -1);
        }
        // System.out.println("要计算日期为:" + sdf.format(cal.getTime())); // 输出要计算日期
        // 设置一个星期的第一天，按中国的习惯一个星期的第一天是星期一
        cal.setFirstDayOfWeek(Calendar.MONDAY);
        // 获得当前日期是一个星期的第几天
        final int day = cal.get(Calendar.DAY_OF_WEEK);
        // 根据日历的规则，给当前日期减去星期几与一个星期第一天的差值
        cal.add(Calendar.DATE, cal.getFirstDayOfWeek() - day);
        final String imptimeBegin = sdf.format(cal.getTime());
        // System.out.println("所在周星期一的日期：" + imptimeBegin);
        cal.add(Calendar.DATE, 6);
        final String imptimeEnd = sdf.format(cal.getTime());
        // System.out.println("所在周星期日的日期：" + imptimeEnd);
        final String weekBegin = imptimeBegin + " 00:00:00";
        final String weekEnd = imptimeEnd + " 23:59:59";

        final SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        final Map<String, Date> map = new HashMap<>();
        try {
            map.put("weekBegin", sdf1.parse(weekBegin));
            map.put("weekEnd", sdf1.parse(weekEnd));
        } catch (final ParseException e) {
            e.printStackTrace();
        }

        return map;
    }

    @Override
    public List<InstitutionsDTO> unstartdInstitutions() {
        final List<InstitutionsDTO> institutions = this.institutionsService.index(new Searchable());

        final Calendar c = Calendar.getInstance();
        int dayOfWeek = c.get(Calendar.DAY_OF_WEEK) - 1;
        if (dayOfWeek == 0) {
            dayOfWeek = 7;
        }
        c.add(Calendar.DATE, -dayOfWeek + 1);
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        final Date start = c.getTime();

        final List<TransferWeekly> list = this.transferWeeklyRepository.findAllByWeekTime(start);
        return institutions.stream()
                .filter(it -> list.stream().filter(w -> it.getCode().equals(w.getUnitCode())).count() == 0)
                .collect(Collectors.toList());
    }

    @Override
    public Map<String, Long> reportStats() {
        final List<InstitutionsDTO> institutions = this.institutionsService.index(new Searchable());

        final Calendar c = Calendar.getInstance();
        int dayOfWeek = c.get(Calendar.DAY_OF_WEEK) - 1;
        if (dayOfWeek == 0) {
            dayOfWeek = 7;
        }
        c.add(Calendar.DATE, -dayOfWeek + 1);
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        final Date start = c.getTime();

        final List<TransferWeekly> list = this.transferWeeklyRepository.findAllByWeekTime(start);

        final Map<String, Long> result = Maps.newHashMap();
        final long reportedCount = list.stream().map(it -> it.getUnitCode()).distinct().count();
        result.put("已提交", reportedCount);
        result.put("未提交", institutions.size() - reportedCount);

        return result;
    }

    @Override
    public Workbook template(final UserBean user) {
        final List<TransferWeeklyImportVO> list = new ArrayList<>();
        final Workbook workbook = ExcelExportUtil.exportExcel(
                new ExportParams(null, DCOMConstants.TRANSFER_IMPORT_TEMPLATE_NAME, ExcelType.XSSF),
                TransferWeeklyImportVO.class,
                list);

        //下拉框候选数据
        final List<Map<String, Object>> constraint = new ArrayList<>();
        final List<String> constraintList = new ArrayList<>();
        final List<InstitutionsDTO> institutions = this.institutionsService.getPermInstitutions(user);
        institutions.stream().map(t -> {
            return t.getName() + DCOMConstants.DICTIONARY_SPLITOR + t.getCode();
        }).forEach(constraintList::add);
        final Map<String, Object> map = new HashMap<>();
        map.put("list", constraintList);
        map.put("col", DCOMConstants.TRANSFER_DAILY_UNIT_NO);
        constraint.add(map);
        EasyPoiExcelUtil.setConstraint(constraint, workbook, DCOMConstants.ASSET_START_CONSTRAINT_ROW_FIRST,
                DCOMConstants.ASSET_CONSTRAINT_ROW_65535);
        return workbook;
    }

    @Override
    @Transactional
    public void importExcel(final InputStream fileStream, final UserBean user) {
        final StringBuilder errorMsg = new StringBuilder();
        final ImportParams params = new ImportParams();
        params.setHeadRows(1);
        params.setNeedVerify(true);
        try {
            final ExcelImportResult<TransferWeeklyImportVO> result =
                    ExcelImportUtil.importExcelMore(fileStream, TransferWeeklyImportVO.class, params);
            if (result.getList().isEmpty() && result.getFailList().isEmpty()) {
                throw new UnitedException("移交服务周报上传数据为空");
            }
            if (result.isVerifyFail()) {
                final List<TransferWeeklyImportVO> failList = result.getFailList();
                failList.forEach(l -> {
                    errorMsg.append("第" + (l.getRowNum() + 1) + "行," + l.getErrorMsg() + "! \n");
                });
            }
            if (errorMsg.length() > 0) {
                TransferWeeklyServiceImpl.log.error(errorMsg.toString());
                throw new UnitedException(errorMsg.toString(), new Object[] {});
            }

            final List<TransferWeeklyImportVO> saveList = result.getList();

            final List<TransferWeekly> models = new ArrayList<>();
            for (int i = 0; i < saveList.size(); i++) {
                models.add(this.transferWeeklyConverter.toModel(saveList.get(i)));
            }
            if (!models.isEmpty() && (errorMsg.length() == 0)) {
                this.transferWeeklyRepository.saveAll(models);
            }

        } catch (final Exception e) {
            TransferWeeklyServiceImpl.log.error("上传失败", e);
            throw new UnitedException(e.getMessage().toString());
        } finally {
            try {
                fileStream.close();
            } catch (final IOException e) {
                throw new UnitedException(e.getMessage().toString());
            }
        }

    }

    @Override
    public TransferWeeklyDTO getLatest(final String unitCode) {
        final TransferWeekly model = this.transferWeeklyRepository.getLatestOne(unitCode);
        return null != model ? this.transferWeeklyConverter.toDTO(model) : null;
    }

    @Override
    public long totalTransfer(final UserBean user) {
        final ProjectUserBean projectInfo = this.institutionsService.getMyInstitutions(user);

        if (PermUtils.isWorker(user)) {
            final Optional<InstitutionsDTO> opt = projectInfo.getInstitutions().stream()
                    .filter(inst -> inst.getCode().equals(projectInfo.getDefaultInstituteCode())).findAny();
            if (opt.isPresent()) {
                final Map<String, Object> query = new HashMap<>();
                query.put("s_submitTo", opt.get().getName());
                final ResultDTO<Long> total = this.dcamMicroService.searchAssetsCount(query);
                return total.getData();
            } else {
                return 0;
            }
        } else if (PermUtils.isWorkerGroupLeader(user)) {
            final LongSummaryStatistics sumcc = projectInfo.getInstitutions().stream().map(inst -> {
                final Map<String, Object> query = new HashMap<>();
                query.put("s_submitTo", inst.getName());
                final ResultDTO<Long> results = this.dcamMicroService.searchAssetsCount(query);
                return results.getData();
            }).collect(Collectors.toList()).stream().collect(Collectors.summarizingLong(e -> e));

            return sumcc.getSum();
        } else {
            final ResultDTO<Long> total = this.dcamMicroService.searchAssetsCount(Maps.newHashMap());
            return total.getData();
        }
    }

}
